# -*- coding: utf-8 -*-
# @author  : Zyp
# @description: 根据源代码文件和路径，实现自动生成目标rst文件路径和doc更新
import os
import shutil
import socket
import subprocess


def create_rst_path(src_file, src_dir, source_dir):
    """
    根据源代码文件和路径，生成目标rst文件路径

    Args:
        src_file (str): 源代码文件路径
        src_dir (str): 源代码目录
        source_dir (str): 源代码目录的 rst 文件目录

    Returns:
        str: 目标rst文件路径
    """
    if not os.path.isabs(src_file) or not os.path.isabs(src_dir):
        raise ValueError('src_file and src_dir should be absolute paths')
    if not src_file.startswith(src_dir):
        raise ValueError('src_file should be under src_dir')
    relative_path = os.path.relpath(src_file, src_dir)
    rst_file = os.path.join(source_dir, os.path.splitext(relative_path)[0] + '.rst')
    return rst_file


def write_index_rst(src_dir, source_dir):
    """
    生成 index.rst 文件，映射到所有的 Python 文件

    Args:
        src_dir (str): 源代码目录
        source_dir (str): 源代码目录的 rst 文件目录
    """
    index_content = []
    index_content += [
        # "Start",
        # "==================",
        # "* :ref:`quick_start`", "",
        "Content",
        "==================",
        ".. toctree::",
        "   :maxdepth: 2", "",
        "   README"
    ]

    for root, _, files in os.walk(src_dir):
        for file in files:
            if file.endswith(".py") and file != "__init__.py":
                src_file = os.path.join(root, file)
                if not has_exported_members(src_file):
                    continue
                rst_file = create_rst_path(src_file, src_dir, source_dir)
                rst_path_relative = os.path.relpath(rst_file, source_dir).replace("\\", "/")
                index_content.append(f"   {rst_path_relative}")

    index_content += [
        "\nIndices and Tables",
        "==================",
        "* :ref:`genindex`",
        "* :ref:`modindex`",
        "* :ref:`search`"
    ]
    index_path = os.path.join(source_dir, 'index.rst')
    with open(index_path, 'w', encoding='utf-8') as f:
        f.write("\n".join(index_content))


def generate_rst_files(src_dir, source_dir):
    """
    遍历 src_dir 生成相应的 rst 文件，映射目录层级
    @param src_dir: 源代码目录
    @param source_dir: 源代码目录的 rst 文件目录
    @return: None
    """
    """
    遍历 src_dir 生成相应的 rst 文件，映射目录层级

    Args:
        src_dir (str): 源代码目录
        source_dir (str): 源代码目录的 rst 文件目录
    """
    for root, _, files in os.walk(src_dir):
        for file in files:
            if file.endswith(".py") and file != "__init__.py":
                src_file = os.path.join(root, file)
                if not has_exported_members(src_file):
                    continue
                rst_file = create_rst_path(str(src_file), src_dir, source_dir)

                # 创建 rst 文件的文件夹
                os.makedirs(os.path.dirname(rst_file), exist_ok=True)

                # 更新 generate_rst_files 函数中的 rst 文件内容写入部分
                module_name = os.path.relpath(str(src_file), src_dir).replace(os.sep, ".")[:-3]
                with open(rst_file, 'w', encoding='utf-8') as f:
                    f.write(f"{module_name}\n{'=' * len(module_name)}\n\n")
                    f.write(f".. automodule:: {module_name}\n")
                    f.write("   :members:\n")
                    f.write("   :undoc-members:\n")
                    f.write("   :private-members:\n")
                    f.write("   :special-members: __init__, __str__\n")

    # 将 README.rst 文件拷贝到 source_dir
    readme_path = os.path.join(os.path.dirname(src_dir), "README.md")
    # readme_path = os.path.join(src_dir, "README.md")
    shutil.copy(readme_path, source_dir)


def build_doc(source_dir, doc_dir, open_browser=True):
    """
    使用 sphinx-autobuild 进行文档实时编译和 HTML 生成

    Args:
        source_dir (str): 源代码目录
        doc_dir (str): 文档目录
        open_browser (bool, optional): 是否在浏览器中打开，默认为 True
    """
    build_dir = os.path.join(doc_dir, "build")
    if os.path.exists(build_dir):
        shutil.rmtree(build_dir)
        print(f"Cleaned build directory: {build_dir}")

    os.chdir(doc_dir)
    local_ip = socket.gethostbyname(socket.gethostname())
    command = ["python", "-m", "sphinx_autobuild", source_dir, build_dir, "--host", local_ip]
    if open_browser:
        command.append("--open-browser")
    try:
        ret = subprocess.run(command, check=True)  # capture_output=True
        print(ret.stdout.decode())
    except subprocess.CalledProcessError as e:
        print(f"Error occurred while building docs: {e}")


# 自定义装饰器，用于标记需要导出到 Sphinx 文档的方法和函数
def export_to_docs(func):
    func._export_to_docs = True
    # 其他代码...
    return func


def has_exported_members(src_file):
    """
    检查 Python 文件中是否存在被 export_to_docs 装饰的成员
    """
    import ast
    with open(src_file, 'r', encoding='utf-8') as f:
        tree = ast.parse(f.read())
    for node in ast.walk(tree):
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
            for decorator in node.decorator_list:
                decorator_id = getattr(decorator, 'id', None)
                if isinstance(decorator, ast.Name) and decorator_id == 'export_to_docs':
                    return True
    return False


def main():
    base_dir = os.path.dirname(os.path.abspath(__file__))
    doc_dir = os.path.join(base_dir, "doc")
    src_dir = os.path.join(base_dir, "src")  # r'D:\Developer\SphinxAuto\src'
    doc_source_dir = os.path.join(doc_dir, "source")  # r"D:\Developer\SphinxAuto\doc\source"

    write_index_rst(src_dir, doc_source_dir)
    generate_rst_files(src_dir, doc_source_dir)
    print("Sphinx rst files generated successfully!")

    build_doc(doc_source_dir, doc_dir)


if __name__ == "__main__":
    main()
